﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Net.Http;
using System.Net;
using System.Web.Http.OData.Query;
using NHibernate.Collection.Generic;

using Inovout.Cloud.DataStore.Web.Models;
using Inovout.Cloud.DataStore.Models;
using Inovout.Cloud.DataStore.Services;
using Inovout.Cloud.DataStore.Web.Utility;

using Inovout.Cloud.Web.OData;
using Microsoft.Data.Edm;
using System.Web.Http.OData;
using Inovout.Models;
using Inovout.Runtime;


namespace Inovout.Cloud.DataStore.Web.Controllers
{
    public class EntityController : DataStoreController
    {
        private IDatabaseService databaseService;
        private IEntityService entityService = null;
        private ITableService tableService = null;

        private const string totalRecordsDatabaseName = "TotalRecordsDatabaseName";
        private const string totalRecordsTableName = "TotalRecordsTableName";
        private const string totalRecordsPrimaryKey = "TotalRecordsPrimaryKey";

        public EntityController()
        {
            this.databaseService = ComponentRegistry.Resolve<IDatabaseService>();
            this.entityService = ComponentRegistry.Resolve<IEntityService>();
            this.tableService = ComponentRegistry.Resolve<ITableService>();
        }

        //GET http://localhost:10230/v1/e/p/{projectName}/t/{tableName}
        [Route("v1/db/{databaseName}/t/{tableName}/e")]
        public string Get(string databaseName, string tableName)
        {
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return null;
            }

            //取得DataStore中的所有数据
            var query = entityService.QueryAll(databaseName, tableName);
            //return query;
            return JsonUtility.ToJson(query);
        }

        [Route("v1/db/{databaseName}/t/{tableName}/e2")]
        public IQueryable Get2(string databaseName, string tableName)
        {
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return null;
            }

            //取得DataStore中的所有数据
            var query = entityService.QueryAll(databaseName, tableName);
            return query;
        }

        //Query http://localhost:10230/v1/e/Query/p/{projectName}/t/{tableName}
        [Route("v1/db/{databaseName}/t/{tableName}/e/query")]
        [HttpGet]
        [ActionName("Get")]
        public string Query(string databaseName, string tableName)
        {
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return null;
            }

            //取得DataStore中的所有数据
            var query = entityService.QueryAll(databaseName, tableName);
            IEdmModel edmModel = ODataModelHelper.GetEdmModel(query);

            //重新构造ODataQueryOptions
            var options = new ODataQueryOptions(new ODataQueryContext(edmModel, query.ElementType), this.Request);

            //使用ODataQueryOptions对取得的数据进行过滤
            var result = options.ApplyTo(query);

            return JsonUtility.ToJson(result);
        }

        //POST http://localhost:10230/v1/e/p/{projectName}/t/{tableName}
        [Route("v1/db/{databaseName}/t/{tableName}/e")]
        public void Put(string databaseName, string tableName, object obj)
        {
            if (obj == null)
            {
                return;
            }

            //取得要被插入记录的表
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return;
            }


            //取得Request Header的Content-Type
            string contentType = this.Request.Content.Headers.ContentType.MediaType;
            switch (contentType)
            {
                case "application/json":

                    try
                    {
                        //构造要插入的数据结构（Key-Value）
                        Dictionary<string, object> rowEntity = JsonUtility.DictionaryFromJson(obj.ToString());

                        if (rowEntity.Count > 0)
                        {
                            entityService.SaveEntity(databaseName, tableName, rowEntity);
                        }
                    }
                    catch
                    {
                        Newtonsoft.Json.Linq.JArray jsonArray = Newtonsoft.Json.Linq.JArray.Parse(obj.ToString());
                        if (jsonArray == null)
                        {
                            return;
                        }

                        IEnumerator<Newtonsoft.Json.Linq.JToken> jTokenList = jsonArray.GetEnumerator();
                        if (jTokenList == null)
                        {
                            return;
                        }

                        List<Dictionary<string, object>> rowEntities = new List<Dictionary<string, object>>();
                        Dictionary<string, object> dicObject = null;
                        while (jTokenList.MoveNext())
                        {
                            dicObject = JsonUtility.DictionaryFromJson(jTokenList.Current.ToString());
                            if (dicObject != null)
                            {
                                rowEntities.Add(dicObject);
                            }
                        }

                        if (rowEntities != null && rowEntities.Count > 0)
                        {
                            entityService.SaveEntities(databaseName, tableName, rowEntities);
                        }
                    }

                    break;
                case "application/xml":
                    //TODO:解析XML格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                case "application/atom+xml":
                    //TODO:解析Atom格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                default:
                    break;
            }
        }

        //PUT http://localhost:10230/v1/e/p/{projectName}/t/{tableName}
        [Route("v1/db/{databaseName}/t/{tableName}/e")]
        public void Post(string databaseName, string tableName, object obj)
        {
            if (obj == null)
            {
                return;
            }

            //取得要被插入记录的表
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return;
            }


            //取得Request Header的Content-Type
            string contentType = this.Request.Content.Headers.ContentType.MediaType;
            switch (contentType)
            {
                case "application/json":

                    try
                    {
                        //构造要插入的数据结构（Key-Value）
                        Dictionary<string, object> rowEntity = JsonUtility.DictionaryFromJson(obj.ToString());

                        if (rowEntity.Count > 0)
                        {
                            entityService.InsertEntity(databaseName, tableName, rowEntity);
                        }
                    }
                    catch
                    {
                        Newtonsoft.Json.Linq.JArray jsonArray = Newtonsoft.Json.Linq.JArray.Parse(obj.ToString());
                        if (jsonArray == null)
                        {
                            return;
                        }

                        IEnumerator<Newtonsoft.Json.Linq.JToken> jTokenList = jsonArray.GetEnumerator();
                        if (jTokenList == null)
                        {
                            return;
                        }

                        List<Dictionary<string, object>> rowEntities = new List<Dictionary<string, object>>();
                        Dictionary<string, object> dicObject = null;
                        while (jTokenList.MoveNext())
                        {
                            dicObject = JsonUtility.DictionaryFromJson(jTokenList.Current.ToString());
                            if (dicObject != null)
                            {
                                rowEntities.Add(dicObject);
                            }
                        }

                        if (rowEntities != null && rowEntities.Count > 0)
                        {
                            entityService.InsertEntitiesBatch(databaseName, tableName, rowEntities);
                        }
                    }

                    break;
                case "application/xml":
                    //TODO:解析XML格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                case "application/atom+xml":
                    //TODO:解析Atom格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                default:
                    break;
            }


        }

        //DELETE http://localhost:10230/v1/e/p/{projectName}/t/{tableName}/k/{key}
        [Route("v1/db/{databaseName}/t/{tableName}/e/{key}")]
        public void Delete(string databaseName, string tableName, string key)
        {
            entityService.DeleteEntity(databaseName, tableName, key);
        }

        [Route("v1/db/{databaseName}/t/{tableName}/e")]
        public void Delete(string databaseName, string tableName, object obj)
        {

            //取得要删除记录的表
            Table table = GetTable(databaseName, tableName);
            if (String.IsNullOrEmpty(databaseName) || table == null)
            {
                return;
            }


            //取得Request Header的Content-Type
            string contentType = this.Request.Content.Headers.ContentType.MediaType;
            switch (contentType)
            {
                case "application/json":

                    //构造删除条件（Key-Value）
                    Dictionary<string, object> condition = JsonUtility.DictionaryFromJson(obj.ToString());

                    if (condition != null && condition.Count > 0)
                    {
                        entityService.DeleteEntity(databaseName, tableName, condition);
                    }

                    break;
                case "application/xml":
                    //TODO:解析XML格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                case "application/atom+xml":
                    //TODO:解析Atom格式
                    //Deserialize(tableType, obj.ToString());
                    break;
                default:
                    break;
            }
        }

        [Route("v1/e/TotalRecords")]
        [HttpGet]
        [ActionName("Get")]
        public long TotalRecords()
        {
            string databaseName = null;
            string tableName = null;
            string primaryKey = null;
            GetTotalRecordsInfo(ref databaseName, ref tableName, ref primaryKey);

            long totalRecords = 0;
            var mongodbClient = ComponentRegistry.Resolve<EntityService>().MongoDbClient;

            var entity = mongodbClient.FindOneByObjectId(databaseName, tableName, primaryKey);
            if (entity == null)
            {
                return 0;
            }
            totalRecords = entity["CountValue"].ToInt64();

            long? id = null;
            if (this.Request.RequestUri.ParseQueryString().AllKeys.Contains("id"))
            {
                id = long.Parse(this.Request.RequestUri.ParseQueryString().Get("id"));
            }
            var sr = this.Request.RequestUri.OriginalString;

            if (id.HasValue && id.Value >= totalRecords)
            {
                Random random = new Random();
                totalRecords = id.Value + random.Next(1, 100);
            }
            return totalRecords;
        }
        #region 私有方法
        /// <summary>
        /// 取得Table
        /// </summary>
        /// <param name="projectName">项目名</param>
        /// <param name="tableName">表名</param>
        /// <returns></returns>
        private Table GetTable(string projectName, string tableName, out string databaseName)
        {
            if (String.IsNullOrEmpty(projectName) || String.IsNullOrEmpty(tableName))
            {
                databaseName = null;
                return null;
            }

            //取得当前项目的数据库
            Database database = GetDatabase(projectName);
            if (database.Tables == null)
            {
                databaseName = null;
                return null;
            }

            //判断表是否存在
            PersistentGenericSet<Table> tables = (PersistentGenericSet<Table>)database.Tables;
            Table table = tables.Where(t => StringUtility.IsEqual(t.Name, tableName)).FirstOrDefault();
            databaseName = projectName;
            return table;
        }

        /// <summary>
        /// 取得Table
        /// </summary>
        /// <param name="databaseName">数据库名</param>
        /// <param name="tableName">表名</param>
        /// <returns></returns>
        private Table GetTable(string databaseName, string tableName)
        {
            if (String.IsNullOrEmpty(databaseName) || String.IsNullOrEmpty(tableName))
            {
                databaseName = null;
                return null;
            }

            //取得当前项目的数据库
            Database database = GetDatabase(databaseName);
            if (database.Tables == null)
            {
                databaseName = null;
                return null;
            }

            //判断表是否存在
            PersistentGenericSet<Table> tables = (PersistentGenericSet<Table>)database.Tables;
            Table table = tables.Where(t => StringUtility.IsEqual(t.Name, tableName)).FirstOrDefault();
            return table;
        }

        /// <summary>
        /// 取得Json字符串中的各个参数的值
        /// </summary>
        /// <param name="propertyValue">Json Value</param>
        /// <returns></returns>
        private Object GetPropertyValue(Newtonsoft.Json.Linq.JToken propertyValue)
        {
            if (propertyValue == null)
            {
                return null;
            }

            var propertyType = propertyValue.Type;
            switch (propertyType)
            {
                case Newtonsoft.Json.Linq.JTokenType.Object:
                    return propertyValue.ToObject(typeof(System.Object));
                case Newtonsoft.Json.Linq.JTokenType.Array:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.Constructor:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.Property:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.Comment:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.Integer:
                    return propertyValue.ToObject(typeof(System.Int32));
                case Newtonsoft.Json.Linq.JTokenType.Float:
                    return propertyValue.ToObject(typeof(System.Double));
                case Newtonsoft.Json.Linq.JTokenType.String:
                    return propertyValue.ToObject(typeof(System.String));
                case Newtonsoft.Json.Linq.JTokenType.Boolean:
                    return propertyValue.ToObject(typeof(System.Boolean));
                case Newtonsoft.Json.Linq.JTokenType.Null:
                    return null;
                case Newtonsoft.Json.Linq.JTokenType.Undefined:
                    return null;
                case Newtonsoft.Json.Linq.JTokenType.Date:
                    return propertyValue.ToObject(typeof(System.DateTime));
                case Newtonsoft.Json.Linq.JTokenType.Raw:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.Bytes:
                    return propertyValue.ToObject(typeof(System.Byte[]));
                case Newtonsoft.Json.Linq.JTokenType.Guid:
                    return propertyValue.ToObject(typeof(System.Guid));
                case Newtonsoft.Json.Linq.JTokenType.Uri:
                    //TODO
                    return propertyValue.ToString();
                case Newtonsoft.Json.Linq.JTokenType.TimeSpan:
                    return propertyValue.ToObject(typeof(System.TimeSpan));
                default:
                    return propertyValue.ToString();
            }
        }

        /// <summary>
        /// 将指定的xml格式的字符反序列化为对应的对象并返回。
        /// </summary>
        /// <param name="type">对象的类型</param>
        /// <param name="xml">待反序列化的xml格式的字符的内容</param>
        /// <returns>返回对应的对象</returns>
        private Object Deserialize(Type type, string xml)
        {
            Object obj = null;
            try
            {
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(type);
                using (System.IO.MemoryStream mem = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(xml)))
                {
                    obj = serializer.Deserialize(mem);
                }
            }
            catch { obj = null; }
            return obj;
        }

        /// <summary>
        /// 将指定的对象序列化为XML格式的字符串并返回。
        /// </summary>
        /// <param name="obj">待序列化的对象</param>
        /// <returns>返回序列化后的字符串</returns>
        private string Serialize(Object obj)
        {
            string xml = "";
            try
            {
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
                using (System.IO.MemoryStream mem = new System.IO.MemoryStream())
                {
                    using (System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(mem, System.Text.Encoding.UTF8))
                    {
                        writer.Formatting = System.Xml.Formatting.Indented;
                        System.Xml.Serialization.XmlSerializerNamespaces n = new System.Xml.Serialization.XmlSerializerNamespaces();
                        n.Add("", "");
                        serializer.Serialize(writer, obj, n);

                        mem.Seek(0, System.IO.SeekOrigin.Begin);
                        using (System.IO.StreamReader reader = new System.IO.StreamReader(mem))
                        {
                            xml = reader.ReadToEnd();
                        }
                    }
                }
            }
            catch { xml = ""; }
            return xml;
        }

        private static void GetTotalRecordsInfo(ref string databaseName, ref string tableName, ref string primaryKey)
        {

            IApplication application = ComponentRegistry.Resolve<IApplication>();
            if (application.Settings.Contains(totalRecordsDatabaseName))
            {
                databaseName = application.Settings[totalRecordsDatabaseName].ToString();
            }
            else
            {
                databaseName = "3Cloud";
            }
            if (application.Settings.Contains(totalRecordsTableName))
            {
                tableName = application.Settings[totalRecordsTableName].ToString();
            }
            else
            {
                tableName = "Records_TotalRocords";
            }
            if (application.Settings.Contains(totalRecordsPrimaryKey))
            {
                primaryKey = application.Settings[totalRecordsPrimaryKey].ToString();
            }
            else
            {
                primaryKey = "TotalRecords";
            }
        }

        #endregion

    }
}